using System.Collections.Generic;
using UnityEngine;
using System;
using System.Linq;
using System.Threading.Tasks;
using UnityEditor.Experimental.GraphView;
using UnityEngine.UIElements;

namespace XLFrame.NodeEditor
{
    /// <summary>
    /// 节点的基类
    /// 初始化生命周期
    /// InitializeForFirstCreation 如果是新节点才调用
    /// Init 每次实例化都会调用
    /// OnDataLoadingEnd 恢复节点,所有节点加载完成后,GetOutputPort()等端口方法在此处才能加载完毕
    /// OnAddView 当节点被添加到视图中下一帧调用,节点宽高位置等信息都已经加载完毕
    /// </summary>
    public abstract class  NodeViewBase : Node, IMenuWindowData,INodeViewFrame
    {
        /// <summary>
        /// 当此节点被选中时
        /// </summary>
        public event Action<NodeViewBase> OnNodeSelectedEvent;
        /// <summary>
        /// 当此节被双击时
        /// </summary>
        public event Action<NodeViewBase> OnNodeDoubleClickedEvent;
        

        /// <summary>
        /// 当此节点被连接时,[Edge,IsInput(是自己的输入接口还是输出接口)]]
        /// </summary>
        public event Action<EdgeLine,bool> OnEdgeConnectedEvent;

        /// <summary>
        /// 当此节点加载数据结束时
        /// </summary>
        public event Action OnNodeDataLoadingDataEndEvent;

        /// <summary>
        /// 当初始化结束时调用
        /// </summary>
        public event Action OnInitEndEvent;
        
        /// <summary>
        /// 当节点添加到视图中时调用(主要用于获取节点当前位置)
        /// </summary>
        public event Action OnNodeAddViewEvent;

        /// <summary>
        /// 节点端口相关操作
        /// </summary>
        public NodeViewPort NodePort { get; private set; }

        /// <summary>
        /// 节点逻辑相关操作
        /// </summary>
        public NodeViewLogic NodeLogic { get;private set; }
        
        /// <summary>
        /// 节点的样式相关操作
        /// </summary>
        public NodeViewStyle NodeStyle { get; private set; }

        /// <summary>
        /// 节点的基础数据
        /// </summary>
        public NodeDataBase NodeData { get;private set; }
        
        /// <summary>
        /// 节点所在的视图
        /// </summary>
        public NodeGraphViewBase NodeGraphView { get;private set; }

        /// <summary>
        /// 右键菜单路径
        /// </summary>
        public virtual string MenuPath { get; private set; }

        /// <summary>
        /// 是新的节点,不是存档加载的节点,值更新于创建节点时,早于所有值
        /// </summary>
        public bool IsNewNode { get; private set; }

        /// <summary>
        /// 标题组件
        /// </summary>
        private Label m_title;

        #region 初始化相关
        protected NodeViewBase()
        {
            NodePort = new NodeViewPort(this);
            NodeLogic = new NodeViewLogic(this,OnExecuteEnd);
            
        }

        
        
        /// <summary>
        /// 通过数据加载节点  !!构建节点时调用必须!!
        /// </summary>
        /// <param name="nodeDataBase"></param>
        private void LoadDataCreateNode(NodeDataBase nodeDataBase)
        {
            NodeData = nodeDataBase;
            NodeStyle = new NodeViewStyle(this);
            PInit();
        }
        
        /// <summary>
        /// 通过数据进行加载,必须是NodeViewBase的子类
        /// </summary>
        /// <returns></returns>
        public static NodeViewBase CreateNode(NodeDataBase nodeData,NodeGraphViewBase nodeGraphViewBase)
        {
            Type t = Type.GetType(nodeData.NodeType);
            if (t != null && t.IsSubclassOf(typeof(NodeViewBase)))
            {
                object instance = Activator.CreateInstance(t);
                if (instance is NodeViewBase component)
                {
                    component.NodeGraphView = nodeGraphViewBase;
                    component.LoadDataCreateNode(nodeData);
                    return component;
                }
            }
            return null;
        }
        
        
        
        
        /// <summary>
        /// 创建新节点,包含新数据 !!构建节点时调用必须!!
        /// </summary>
        void INodeViewFrame.CreateNode(NodeGraphViewBase nodeGraphViewBase)
        {
            NodeGraphView = nodeGraphViewBase;
            IsNewNode = true;
            NodeData = new NodeDataBase();
            NodeStyle = new NodeViewStyle(this);
            Type type = GetType();
            NodeData.NodeType = $"{type.FullName}, {type.Assembly.GetName().Name}";
            InitializeForFirstCreation();
            PInit();
        }
       
        
        
        
        /// <summary>
        /// 第一次创建时调用,用于定义节点端口或者其他逻辑 仅在创建存档时调用[在Init调用之前]
        /// </summary>
        protected virtual void InitializeForFirstCreation()
        {
        }
        
        /// <summary>
        /// 私有初始化方法
        /// </summary>
        private void PInit()
        {
            OnEdgeConnectedEvent += NodeViewBase_OnEdgeConnected;
            OnNodeDataLoadingDataEndEvent += OnDataLoadingEnd;
            OnNodeAddViewEvent += OnAddView;
            NodePort.OnInputPortDataChangeEvent += OnInputViewDataUpdate;
            NodePort.OnPortDataChangeEvent += OnViewDataUpdate;
            NodePort.OnOutputPortDataChangeEvent += OnOutputViewDataUpdate;
            NodePort.OnRuntimeCreatePortEvent += OnRuntimeCreatePort;
            NodePort.OnRuntimePortRemoveEvent += OnRuntimePortRemove;

            this.RegisterCallback<MouseDownEvent>(OnMouseDown);
            
            m_title = this.Q<Label>("title-label", (string)null);

            Init();
            
            OnInitEndEvent?.Invoke();
            
            if (string.IsNullOrEmpty(MenuPath))
                MenuPath = $"其他/{title}";

            RefreshExpandedState();
            RefreshPorts();
        }
        
        /// <summary>
        /// 构建节点时调用,每次实例化节点时都会调用,无论是第一次还是加载存档,用于改变样式等操作
        /// </summary>
        protected virtual void Init()
        {
            title = GetType().Name;
            extensionContainer.style.backgroundColor = new Color(0.2f, 0.2f, 0.2f);
        }
        #endregion
        
        #region 逻辑层相关

        protected virtual void OnExecuteEnd(INodeLogic nodeLogic)
        {
        
        }
        
        #endregion

        #region 右键端口相关

        /// <summary>
        /// 推荐端口缓存,用于快速查找推荐连接的端口
        /// </summary>
        private Dictionary<Type, Port> m_inputRecommendedPort = new Dictionary<Type, Port>();
        private Dictionary<Type, Port> m_outputRecommendedPort = new Dictionary<Type, Port>();

        /// <summary>
        /// 当前节点推荐的端口连接节点
        /// </summary>
        private HashSet<Type> m_recommendedPortNodeType = new HashSet<Type>();
        private HashSet<Type> m_inputRecommendedPortNodeType = new HashSet<Type>();
        private HashSet<Type> m_outputRecommendedPortNodeType = new HashSet<Type>();
        
        /// <summary>
        /// 接口的初始化数据使用,由右键菜单查询类属性用
        /// </summary>
        public virtual void InitData()
        {
           ((INodeViewFrame) this).CreateNode(NodeGraphView);
        }
        
        /// <summary>
        /// 添加右键菜单推荐的端口节点(输入输出全部显示)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void AddRecommendedPortNode<T>() where T : IMenuWindowData
        {
            m_recommendedPortNodeType.Add(typeof(T));
        }
        /// <summary>
        /// 添加右键菜单推荐的端口节点仅支持输入
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void AddInputRecommendedPortNode<T>() where T : IMenuWindowData
        {
            m_inputRecommendedPortNodeType.Add(typeof(T));
        }
        /// <summary>
        /// 添加右键菜单推荐的端口节点仅支持输出
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void AddOutputRecommendedPortNode<T>() where T : IMenuWindowData
        {
            m_outputRecommendedPortNodeType.Add(typeof(T));
        }

        /// <summary>
        /// 获取推荐的端口节点
        /// </summary>
        /// <returns></returns>
        public List<Type> GetRecommendedPortNode(bool isInput)
        {
            List<Type> ret = new List<Type>(m_recommendedPortNodeType.ToList());
            ret.AddRange(isInput? m_inputRecommendedPortNodeType: m_outputRecommendedPortNodeType);
            return ret;
        }


        /// <summary>
        /// 输入端口,返回推荐连接的端口
        /// 此方法已完成,动态增加端口可兼容,
        /// 调用此方法,基本是右键拉出选择新的节点后,查找推荐的端口连接时调用,不存在移除和运行时添加端口的情况都是默认情况
        /// 其次就算是运行时添加了新的端口,但是在运行时不会再次调用这个方法了,这个方法仅在创建节点时调用,不管是输入还是输出端口拉出都只会调用新的节点的推荐连接方法
        /// 
        /// 已确定不会出现 [但是删减端口还会存在缓存需要移除端口事件时移除缓存, 否则可能的状况,返回null值]
        /// </summary>
        /// <param name="port"></param>
        /// <param name="link"></param>
        /// <returns></returns>
        public Port ConnectRecommendedPort(Port port,bool link=false)
        {
            bool isInput = port.direction == Direction.Input;

            Dictionary<Type, Port> recommendedPortDict = isInput ? m_inputRecommendedPort : m_outputRecommendedPort;

            if (!recommendedPortDict.TryGetValue(port.portType, out var ret))
            {
                
                bool isExecutionLine = port.portType == typeof(ExecutionLine);
                Dictionary<string, Port> portDict = isInput ? NodePort.OutputPort : NodePort.InputPort;

                foreach (var item in portDict)
                {
                    if (isExecutionLine ^ item.Value.portType == typeof(ExecutionLine))
                        continue;

                    if ((isInput ? item.Value.portType.IsSubclassOf(port.portType) : 
                            port.portType.IsSubclassOf(item.Value.portType)) || item.Value.portType == port.portType)
                    {
                        ret = item.Value;
                        recommendedPortDict.Add(port.portType, ret);
                        break;
                    }
                }
            }
            
            if (link) NodePort.LinkNodes(!isInput ? port : ret, isInput ? port : ret);
            return ret;
        }

        #endregion

        #region 连接相关


        /// <summary>
        /// 有边连接时调用
        /// </summary>
        /// <param name="edge"></param>
        /// <param name="IsInput">自己是input</param>
        public void ConnectEdgeEvent(EdgeLine edge,bool IsInput)
        {
            OnEdgeConnectedEvent?.Invoke(edge, IsInput);
        }


        /// <summary>
        /// 当边连接时调用
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="IsInput"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void NodeViewBase_OnEdgeConnected(EdgeLine obj, bool IsInput)
        {
            // 同步连接数据
            if(IsInput)
            {
                // 自己的输入接入
            }
            else
            {
                // 自己的输出对象 保存此输出接口的连接对象
                if (obj.input.node is NodeViewBase inputNode)
                {
                    var data = new NodeLinkData(inputNode.NodeData.GUID, obj.input.name);
                    data.CreateEdgeGuid();
                    obj.name = data.EdgeGuid;
                    NodeData.GetOutputPort(obj.output.name).Add(data);
                }
                
                // 将输出端的数据放入到连接的输入端
                // 拿到连线数据
                if (obj.input.userData is not PortData inputUserData)
                {
                    Debug.LogError("输入端口数据类型错误");
                    return;
                }

                if(obj.output.userData is not PortData outputUserData)
                {
                    Debug.LogError("输出端口数据类型错误");
                    return;
                }
                
                inputUserData.Value = outputUserData;
            }
        }

        #endregion

        #region 事件相关
        
        /// <summary>
        /// [注册自OnRuntimePortRemoveEvent]
        /// 当运行时移除端口时调用
        /// </summary>
        /// <param name="obj"></param>
        private void OnRuntimePortRemove(Port obj)
        {
            Debug.Log("运行时移除"+obj.portName);
        }
        
        
        /// <summary>
        /// [注册自OnRuntimeCreatePortEvent]
        /// 运行时新增端口 
        /// </summary>
        /// <param name="obj"></param>
        private static void OnRuntimeCreatePort(Port obj)
        {
            Debug.Log("运行时创建"+obj.portName);
        }


        /// <summary>
        /// [注册在 OnPortDataChangeEvent]
        /// 当视图数据更新时调用
        /// </summary>
        protected virtual void OnViewDataUpdate(PortDataEvent portData)
        {
            
        }
        
        /// <summary>
        /// [注册在 OnInputPortDataChangeEvent]
        /// 当输入端视图数据更新时调用
        /// </summary>
        protected virtual void OnInputViewDataUpdate(PortDataEvent portData)
        {
            
        }
        /// <summary>
        /// [注册在 OnOutputPortDataChangeEvent]
        /// 当输出端视图数据更新时调用
        /// </summary>
        protected virtual void OnOutputViewDataUpdate(PortDataEvent portData)
        {
            
        }
        
        
        /// <summary>
        /// [注册在 OnNodeAddView]
        /// 当几点被添加到视图中时调用
        /// </summary>
        protected virtual void OnAddView()
        {

        }

        /// <summary>
        /// [注册在 OnNodeDataLoadingDataEnd]
        /// 当前节点加载数据结束时调用 所有节点加载完成后
        /// </summary>
        protected virtual void OnDataLoadingEnd()
        {
            
        }

        /// <summary>
        /// [框架调用]
        /// 加载数据结束
        /// </summary>
        void INodeViewFrame.OnNodeDataLoadingDataEndEvent()
        {
            OnNodeDataLoadingDataEndEvent?.Invoke();
            RefreshExpandedState();
            RefreshPorts();
        }

        /// <summary>
        /// [框架调用]
        /// 当节点被添加到视图中时调用
        /// </summary>
        void INodeViewFrame.OnNodeAddViewEvent()
        {
            OnNodeAddViewEvent?.Invoke();
            
            if (IsNewNode)
            {
                // 新建的节点添加到选择中
                NodeGraphView.AddToSelection(this);
            }
          
            RefreshExpandedState();
            RefreshPorts();
        }
        
        // 用于判断双击
        private long m_DoubleClickTime;
        void OnMouseDown(MouseDownEvent evt)
        {
            var tTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
            if (tTime - m_DoubleClickTime <= 300)
            {
                OnNodeDoubleClickedEvent?.Invoke(this);
            }
            m_DoubleClickTime = tTime;
        }
        
        /// <summary>
        /// [父级调用]
        /// 当节点被选中时调用 
        /// </summary>
        public override void OnSelected()
        {
            base.OnSelected();
            OnNodeSelectedEvent?.Invoke(this);
        }
        
        #endregion
        
        #region 其他工具
        
        public override Port InstantiatePort(Orientation   orientation, Direction direction,
                                             Port.Capacity capacity,    Type      type)
        {
            var line = Port.Create<EdgeLine>(orientation, direction, capacity, type);
            return line;
        }
        
        public void SetPosition(Vector2 pos)
        {
            SetPosition(new Rect(pos, Vector2.one));
            NodeData.Position = pos;
            NodeGraphView.DataUpdate();
        }

        /// <summary>
        /// 下一帧执行
        /// </summary>
        /// <param name="unityAction"></param>
        public void ScheduleDelayedAction(Action unityAction)
        {
            schedule.Execute(unityAction).StartingIn(1); // 延迟1帧执行
        }

        /// <summary>
        /// 16进制颜色转Color
        /// </summary>
        /// <param name="hex"></param>
        /// <returns></returns>
        public static Color HexToColor(string hex)
        {
            Color color = default;
            if (ColorUtility.TryParseHtmlString(hex, out color))
            {
                return color;
            }
            else
            {
                Debug.LogError("Invalid hex color string");
                return Color.black; // 返回黑色作为默认值或错误值
            }
        }

        #endregion

        #region 样式相关

        protected void StyleSetBackgroundColor(Color color)
        {
            extensionContainer.style.backgroundColor = color;
        }
        
        protected void StyleSetBorderColor(Color color)
        {
            extensionContainer.style.borderBottomColor = color;
            extensionContainer.style.borderLeftColor = color;
            extensionContainer.style.borderRightColor = color;
            extensionContainer.style.borderTopColor = color;
        }
        
        protected void StyleSetBorderWidth(float width)
        {
            extensionContainer.style.borderBottomWidth = width;
            extensionContainer.style.borderLeftWidth = width;
            extensionContainer.style.borderRightWidth = width;
            extensionContainer.style.borderTopWidth = width;
        }
        
        protected void StyleSetBorderRadius(float radius)
        {
            extensionContainer.style.borderBottomLeftRadius = radius;
            extensionContainer.style.borderBottomRightRadius = radius;
            extensionContainer.style.borderTopLeftRadius = radius;
            extensionContainer.style.borderTopRightRadius = radius;
        }

        protected void StyleSetTitleBackgroundColor(Color color)
        {
            titleContainer.style.backgroundColor = color;
            // 保证文字清晰

            m_title.style.color = Color.white;
            m_title.style.textShadow = new TextShadow()
            {
                offset = new Vector2(1, 1), // Adjust the offset as needed
                blurRadius = 0,             // No blur for a sharp outline
                color = Color.black         // Outline color
            };
        }
        public static Color GetContrastingTextColor(Color backgroundColor)
        {
            // Calculate the luminance of the background color
            float luminance = (0.299f * backgroundColor.r + 0.587f * backgroundColor.g + 0.114f * backgroundColor.b);

            // Return black or white based on the luminance
            return luminance > 0.5f ? Color.black : Color.white;
        }

        /// <summary>
        /// 设置高亮显示1秒
        /// </summary>
        public void SetHighlight()
        {
           // extensionContainer
           // titleContainer
           
           StyleColor extensionContainerColor = extensionContainer.style.backgroundColor;
           StyleColor titleContainerColor = titleContainer.style.backgroundColor;
           
           Color color = Color.yellow;
           
           extensionContainer.style.backgroundColor = color;
           titleContainer.style.backgroundColor = color;
           
           DelayedAction.ExecuteDelayedActionStatic(() =>
           {
               extensionContainer.style.backgroundColor = extensionContainerColor;
               titleContainer.style.backgroundColor = titleContainerColor;
           });
           
        }
        
        #endregion
    }
}

/// <summary>
/// 延迟回调
/// </summary>
public class DelayedAction
{
    public async void ExecuteDelayedAction(Action action, int delayMilliseconds = 1000)
    {
        await Task.Delay(delayMilliseconds);
        action?.Invoke();
    }

    public static async void ExecuteDelayedActionStatic(Action action, int delayMilliseconds = 1000)
    {
        await Task.Delay(delayMilliseconds);
        action?.Invoke();
    }
}